package com.wyj.utils

import androidx.arch.core.executor.ArchTaskExecutor
import androidx.databinding.ObservableField
import androidx.lifecycle.MutableLiveData
import com.google.gson.reflect.TypeToken
import com.hjq.gson.factory.GsonFactory
import com.tencent.mmkv.MMKV

class PropEasy(val key: String? = null) {
    companion object {
        @JvmField
        val shared = MMKV.defaultMMKV()
    }

    val onPropChangedCallback = ArrayList<Consumer<Any?>>()
    inline fun <reified T : Any> asLiveData(): MutableLiveData<T> = object : MutableLiveData<T>(get()) {
        init {
            onPropChangedCallback.add { v ->
                if (ArchTaskExecutor.getInstance().isMainThread) {
                    super.setValue(v as? T)
                } else {
                    super.postValue(v as? T)
                }
            }
        }

        @Synchronized
        override fun getValue(): T? = super.getValue() ?: this@PropEasy.get()

        @Synchronized
        override fun setValue(value: T?) {
            super.setValue(value)
            this@PropEasy.set(value)
        }
    }

    inline fun <reified T : Any> asObservableField(): ObservableField<T> = object : ObservableField<T>(get() as? T) {
        init {
            onPropChangedCallback.add { v ->
                super.set(v as? T)
            }
        }

        @Synchronized
        override fun get(): T? = super.get() ?: this@PropEasy.get()

        @Synchronized
        override fun set(value: T?) {
            super.set(value)
            this@PropEasy.set(value)
        }
    }

    @Volatile
    var _value: Any? = null

    inline fun <reified T : Any> set(value: T?) {
        synchronized(this) {
            onPropChangedCallback.forEach {
                it.accept(value)
            }
            if (value == null) {
                delete()
            } else {
                _value = value
                setMMKVValue(value)
            }
        }
    }

    inline fun <reified T : Any> setMMKVValue(value: T?) {
        if (!isNullOrEmpty(key)) {
            when (value) {
                is Boolean -> shared?.encode(key, value)
                is Int -> shared?.encode(key, value)
                is Long -> shared?.encode(key, value)
                is Float -> shared?.encode(key, value)
                is Double -> shared?.encode(key, value)
                is String -> shared?.encode(key, value)
                is ByteArray -> shared?.encode(key, value)
                else -> shared?.encode(key, GsonFactory.getSingletonGson().toJson(value))
            }
        }
    }

    inline fun <reified T : Any> getMMKVValue(): T? {
        return when (T::class.java) {
            java.lang.Boolean::class.java, Boolean::class.java -> shared?.decodeBool(key)
            java.lang.Integer::class.java, Int::class.java -> shared?.decodeInt(key)
            java.lang.Long::class.java, Long::class.java -> shared?.decodeLong(key)
            java.lang.Float::class.java, Float::class.java -> shared?.decodeFloat(key)
            java.lang.Double::class.java, Double::class.java -> shared?.decodeDouble(key)
            java.lang.String::class.java, String::class.java -> shared?.decodeString(key)
            ByteArray::class.java -> shared?.decodeBytes(key)
            else -> {
                var obj: Any? = null
                try {
                    obj = GsonFactory.getSingletonGson().fromJson(shared?.decodeString(key), object : TypeToken<T>() {}.type)
                } catch (e: Exception) {
                }
                obj
            }
        } as? T
    }

    inline fun <reified T : Any> get(): T? {
        if (_value == null && !isNullOrEmpty(key)) {
            _value = getMMKVValue<T>()
        }
        return _value as? T
    }

    @Synchronized
    fun delete() {
        _value = null
        if (!isNullOrEmpty(key)) {
            shared?.remove(key)
        }
    }
}